#!/usr/bin/env bash
set -euo pipefail

cd "$(dirname "$0")"
sandboxes_dir="sandboxes"
codapi_url="${CODAPI_URL:-localhost:1313}"

main() {
    # Check the prerequisites.
    if [[ "$#" -lt 1 || "$1" == "-h"  || "$1" == "--help" ]]; then
        do_help
        exit 0
    fi

    # Route according to the command.
    local command="$1"; shift
    case "$command" in
    "exec")
        do_exec "$@"
        ;;
    "help")
        do_help "$@"
        ;;
    "sandbox")
        do_sandbox "$@"
        ;;
    *)
        echo "Unknown command: $command"
        exit 1
        ;;
    esac
}

do_exec() {
    # Command: codapi-cli exec <sandbox> <command> <filename>

    # Check the prerequisites.
    if [[ "$#" -ne 3 ]]; then
        echo "Usage: $0 exec <sandbox> <command> <filename>"
        exit 1
    fi
    if ! command -v curl >/dev/null 2>&1; then
        echo "ERROR: 'curl' is not installed. Install it and try again."
        exit 1
    fi
    if ! command -v jq >/dev/null 2>&1; then
        echo "ERROR: 'jq' is not installed. Install it and try again."
        exit 1
    fi

    # Read the arguments.
    local sandbox="$1"
    local command="$2"
    local filename="$3"
    if [[ ! -f "$filename" ]]; then
        echo "ERROR: File not found: $filename"
        exit 1
    fi
    local contents
    contents=$(<"$filename")

    # Prepare and send the exec request.
    local json_payload
    json_payload=$(jq -n --arg s "$sandbox" --arg c "$command" --arg f "$contents" '{sandbox: $s, command: $c, files: {"": $f}}')
    local response http_code
    response=$(mktemp)
    http_code=$(curl --silent --show-error --json "$json_payload" --output "$response" --write-out '%{http_code}' "$codapi_url/v1/exec")

    # Check the HTTP status.
    if [[ "$http_code" != "200" ]]; then
        cat "$response"
        rm -f "$response"
        exit 1
    fi

    # Display the response.
    local stdout stderr
    stdout=$(cat "$response" | jq -r '.stdout // empty')
    stderr=$(cat "$response" | jq -r '.stderr // empty')
    rm -f "$response"
    if [[ -n "$stdout" ]]; then
        echo "$stdout"
    fi
    if [[ -n "$stderr" ]]; then
        echo "$stderr"
    fi
}

do_sandbox() {
    local command="$1"; shift
    mkdir -p "$sandboxes_dir"
    case "$command" in
        "add")
            do_sandbox_add "$@"
            ;;
        "rm")
            do_sandbox_rm "$@"
            ;;
        "ls")
            do_sandbox_ls "$@"
            ;;
        *)
            echo "Unknown command: $command"
            exit 1
            ;;
    esac
}

do_sandbox_add() {
    # Command: codapi-cli sandbox add <path>

    local path="${1:-}"
    if [[ -z "$path" ]]; then
        echo "Usage: $0 sandbox add <path>"
        exit 1
    fi

    # 0. Check if the path a package name.
    if [[ "$path" =~ ^[a-zA-Z0-9_-]*$ ]]; then
        # This is a package name, so we need to download it from the registry.
        path="https://github.com/nalgeon/sandboxes/releases/download/latest/$path.tar.gz" 
    fi

    # 1. Set the name of the sandbox.
    local filename
    filename=$(basename "$path")
    local archive_path="$sandboxes_dir/$filename"
    local name
    if [[ "$filename" == *.tar.gz ]]; then
        name="${filename%.tar.gz}"
    else
        echo "ERROR: Archive name must end with .tar.gz"
        rm -f "$archive_path"
        exit 1
    fi

    # 2. Check if the sandbox already exists.
    local target_dir="$sandboxes_dir/$name"
    if [[ -d "$target_dir" ]]; then
        echo "ERROR: Sandbox '$name' already exists"
        echo "Remove it with 'codapi-cli sandbox rm $name' and try again"
        rm -f "$archive_path"
        exit 1
    fi

    # 3. Get the sandbox archive.
    if [[ "$path" == http://* || "$path" == https://* ]]; then
        echo "Downloading from $path..."
        if ! curl --location --progress-bar --output "$sandboxes_dir/$filename" "$path"; then
            echo "ERROR: Failed to download sandbox archive from $path"
            exit 1
        fi
    else
        echo "Copying local file $path..."
        if [[ ! -f "$path" ]]; then
            echo "ERROR: File not found at $path"
            exit 1
        fi
        if ! cp "$path" "$sandboxes_dir/"; then
             echo "ERROR: Failed to copy sandbox archive from $path"
             exit 1
        fi
    fi

    # 4. Extract the archive.
    echo "Extracting $archive_path to $target_dir..."
    mkdir -p "$target_dir"
    if ! tar -xzf "$archive_path" -C "$sandboxes_dir"; then
        echo "ERROR: Failed to extract sandbox archive $archive_path"
        rm -rf "$target_dir"
        rm -f "$archive_path"
        exit 1
    fi
    rm -f "$archive_path"

    # 5. Run build.sh if it exists.
    local build_script="$target_dir/build.sh"
    if [[ -f "$build_script" ]]; then
        echo "Running build script: $build_script"
        if [[ ! -x "$build_script" ]]; then
            echo "Warning: $build_script is not executable, fixing..."
            chmod +x "$build_script"
        fi
        # Execute the script from within its directory
        (cd "$target_dir" && ./build.sh)
        if [[ $? -ne 0 ]]; then
            echo "ERROR: build.sh failed for sandbox '$name'"
            exit 1
        fi
    fi

    # 6. Run setup.sh if it exists.
    local setup_script="$target_dir/setup.sh"
    if [[ -f "$setup_script" ]]; then
        echo "Running setup script: $setup_script"
        if [[ ! -x "$setup_script" ]]; then
            echo "Warning: $setup_script is not executable, fixing..."
            chmod +x "$setup_script"
        fi
         # Execute the script from within its directory
        (cd "$target_dir" && ./setup.sh)
         if [[ $? -ne 0 ]]; then
            echo "ERROR: setup.sh failed for sandbox '$name'"
            exit 1
        fi
    fi

    # 7. Display success message.
    echo "✓ Successfully added sandbox '$name' ($target_dir)"
}

do_sandbox_rm() {
    # Command: codapi-cli sandbox rm <name>

    local name="${1:-}"
    if [[ -z "$name" ]]; then
        echo "Usage: $0 sandbox rm <name>"
        exit 1
    fi

    local target_dir="$sandboxes_dir/$name"
    if [[ ! -d "$target_dir" ]]; then
        echo "ERROR: Sandbox '$name' does not exist"
        exit 1
    fi

    # 1. Remove the container if it exists.
    if docker ps -a -q --filter "name=$name" | grep -q .; then
        echo "Removing container '$name'..."
        docker rm -f "$name"
    fi

    # 2. Remove the directory and its contents.
    echo "Removing sandbox '$name' from $sandboxes_dir..."
    rm -rf "$target_dir"
    echo "✓ Successfully removed sandbox '$name'"
}

do_sandbox_ls() {
    # Command: codapi-cli sandbox ls

    local name
    local count=0
    echo "Available sandboxes:"
    for dir in "$sandboxes_dir"/*; do
        if [[ -d "$dir" ]]; then
            name=$(basename "$dir")
            echo "  - $name ($dir)"
            count=$((count + 1))
        fi
    done
    if [[ $count -eq 0 ]]; then
        echo "(none)"
    else
        echo "($count total)"
    fi
}

do_help() {
    echo "Usage: $0 <command> [args...]"
    echo ""
    echo "commands:"
    echo "  exec            Execute code in a sandbox"
    echo "  sandbox add     Add a new sandbox"
    echo "  sandbox rm      Remove an existing sandbox"
    echo "  sandbox ls      List all sandboxes"
}

main "$@"
